package com.example.export.aspect;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.export.annotation.CusExport;
import com.example.export.config.ExportProperties;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author PC
 * 导出切面
 */
@Aspect
@Component
public class CusExportAspect {
    private final static Logger logger = LoggerFactory.getLogger(CusExportAspect.class);

    private final ExportProperties exportProperties;

    @Autowired
    public CusExportAspect(ExportProperties exportProperties) {
        this.exportProperties = exportProperties;
    }

    @AfterReturning(pointcut = "@annotation(com.example.export.annotation.CusExport)", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) throws IOException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        CusExport cusExport = methodSignature.getMethod().getAnnotation(CusExport.class);
        HttpServletResponse response = getHttpServletResponse();
        // 这里URLEncoder.encode可以防止中文乱码
        String generatorFileName = StringUtils.isEmpty(cusExport.fileName()) ? exportProperties.getDefaultFileName() : cusExport.fileName();
        String fileName = URLEncoder.encode(generatorFileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        if (!cusExport.dynamicFlag()) {
            EasyExcel.write(response.getOutputStream(), cusExport.dataClass())
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(exportProperties.getDefaultSheetName())
                    .doWrite((Collection<?>) result);
        } else {
            this.dealSpecified(cusExport, result, fileName, response);
        }
    }

    private static HttpServletResponse getHttpServletResponse() {
        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attr != null;
        HttpServletResponse response = attr.getResponse();
        assert response != null;
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        return response;
    }

    @SuppressWarnings("unchecked")
    private void dealSpecified(CusExport cusExport, Object result, String fileName, HttpServletResponse response) throws IOException {
        if (Objects.isNull(cusExport) || Objects.isNull(result) || StringUtils.isEmpty(fileName)) {
            logger.error("the required field is missing");
        }
        logger.debug("the data type is not specified");
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (result instanceof List) {
            resultList = (List<Map<String, Object>>) result;
        }
        if (CollectionUtils.isEmpty(resultList)) {
            logger.info("data is empty");
        }
        List<List<String>> headList = new ArrayList<>();
        boolean fillHeadFlag = false;
        for (Map<String, Object> resultItem : resultList) {
            if (!fillHeadFlag) {
                resultItem.keySet().forEach(keyCode -> headList.add(Collections.singletonList(keyCode)));
                fillHeadFlag = true;
            }
        }

        EasyExcel.write(response.getOutputStream())
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // 放入动态头
                .head(headList).sheet(exportProperties.getDefaultSheetName())
                .doWrite(convertMapToList(resultList, headList));
    }

    private List<List<Object>> convertMapToList(List<Map<String, Object>> mapList, List<List<String>> headList) {
        List<List<Object>> list = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            List<Object> info = new ArrayList<>();
            for (List<String> itemHeadList : headList) {
                info.add(map.get(itemHeadList.get(0)));
            }
            list.add(info);
        }
        return list;
    }
}
